/**
 * @description Mypromise
 * @author cyl
 *
 */

class Mypromise {
  state = 'pending'; // 状态 'pending' 'fulfilled' 'rejected'
  value = undefined; // 成功后的值
  reason = undefined; //
  // 如果要是 setTimeout改变状态，就先将回调保存起来
  resolveCallbacks = []; // .then的时候 状态为pending时，存储回调
  rejectCallbacks = [];
  constructor(fn) {
    const resolveHandler = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 状态初始为pending,然后变化后在这里执行 fn
        this.resolveCallbacks.forEach((fn) => fn(this.value));
      }
    };
    const rejectHandler = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.rejectCallbacks.forEach((fn) => fn(this.reason));
      }
    };

    try {
      fn(resolveHandler, rejectHandler);
    } catch (err) {
      rejectHandler(err);
    }
  }

  then(fn1, fn2) {
    fn1 = typeof fn1 === 'function' ? fn1 : (v) => v;
    fn2 = typeof fn2 === 'function' ? fn2 : (err) => err;
    // 状态还没有发生改变，存储fn1,fn2
    if (this.state === 'pending') {
      const p1 = new Mypromise((resolve, reject) => {
        // 保存函数
        // 当状态改变为fulfilled的时候，执行下面的回调
        this.resolveCallbacks.push(() => {
          try {
            const newValue = fn1(this.value);
            resolve(newValue); // p1.value
          } catch (error) {
            reject(error);
          }
        });
        this.rejectCallbacks.push(() => {
          try {
            const newRerson = fn2(this.reason);
            reject(newRerson); // p1.reason
          } catch (error) {
            reject(error);
          }
        });
      });
      return p1;
    }
    // 状态已经改变，直接执行
    if (this.state === 'fulfilled') {
      // 执行完then 返回一个新的promise
      const p1 = new Mypromise((resolve, reject) => {
        try {
          // this.value == resolve(value)里面传的值
          const newValue = fn1(this.value);
          // 返回新的值
          console.log(newValue);
          resolve(newValue);
        } catch (error) {
          reject(error);
        }
      });
      return p1;
    }

    if (this.state === 'rejected') {
      const p1 = new Mypromise((resolve, reject) => {
        try {
          const newReason = fn2(this.reason);
          reject(newReason);
        } catch (error) {
          reject(error);
        }
      });
      return p1;
    }
  }
  // .catch 是 then的语法糖
  catch(fn) {
    return this.then(null, fn);
  }
}

// MyPromise的静态方法
Mypromise.resolve = function (value) {
  return new Mypromise((resolve, reject) => resolve(value));
};

Mypromise.reject = function (value) {
  return new Mypromise((resolve, reject) => reject(reason));
};

Mypromise.all = function (promiseList = []) {
  const p1 = new Mypromise((resolve, reject) => {
    const result = [];
    const length = promiseList.length;
    let resolveCount = 0;

    promiseList.forEach((p) => {
      p.then((data) => {
        result.push(data);
        // resolveCount 必须在 then 里面做 ++
        // 不能用index
        resolveCount++;
        if (resolveCount === length) {
          resolve(result);
        }
      }).catch((err) => {
        reject(err);
      });
    });
  });
  return p1;
};

Mypromise.race = function (promiseList = []) {
  let resolved = false; // 标记
  const p1 = new Promise((resolve, reject) => {
    promiseList.forEach((p) => {
      p.then((data) => {
        if (!resolved) {
          resolve(data);
          resolved = true;
        }
      }).catch((err) => {
        reject(err);
      });
    });
  });
  return p1;
};

Mypromise.allSettled = function (promiseList = []) {
  return new Promise((resolve, reject) => {
    const res = [],
      len = promiseList.length,
      count = len;
    promiseList.forEach((item, index) => {
      item
        .then(
          (res) => {
            res[index] = { status: 'fulfilled', value: res };
          },
          (error) => {
            res[index] = { status: 'rejected', value: error };
          }
        )
        .finally(() => {
          if (!--count) {
            resolve(res);
          }
        });
    });
  });
};
